﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     SMLib\DataTypes\EnStack.cs
 * Description  :      A clonable stack
 * Change log   :     -2011: First version (originally, it was part of SimGine library)
 * To Do        :
 ************************************************************************/
using System;
using System.Collections.Generic;
using SMLib.Base;

namespace SMLib.DataTypes
{

    /// <summary>
    /// A Stack
    /// </summary>
    /// <typeparam name="T">Type of elements inside the stack, It should be string, a primitive type or ICloneable!</typeparam>
    public class EnStack<T> :
        IStateVariable
    {

        List<T> theStack;
        int max;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="maximumSize">the maximum size of stack</param>
        public EnStack(int maximumSize = int.MaxValue)
        {
            if (!(typeof(T).IsPrimitive || (typeof(T) == typeof(string)) || (typeof(T).IsEnum)
                || (typeof(T).GetInterface(typeof(ICloneable).FullName) != null)))
                throw new Exception("The type of T in EnQueue<T> should be built-in, string or ICloneable!");

            this.max = maximumSize;
            theStack = new List<T>();
        }

        /// <summary>
        /// Push into the stacl
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool Push(T value)
        {
            if (theStack.Count == max)
                return false;
            theStack.Add(value);
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        public int Count
        {
            get
            {
                return theStack.Count;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public T Head
        {
            get
            {
                if (theStack.Count == 0)
                    throw new Exception("Empty Stack!");
                return theStack[theStack.Count - 1];
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            theStack.Clear();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public T Pop()
        {
            T t = theStack[Count - 1];
            theStack.RemoveAt(Count - 1);
            return t;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return theStack.Count.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (this.GetType() != obj.GetType())
                return false;
            EnStack<T> secondobj = obj as EnStack<T>;
            if (this.theStack.Count != secondobj.theStack.Count)
                return false;
            for (int i = 0; i < theStack.Count; i++)
            {
                if (!theStack[i].Equals(secondobj.theStack[i]))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            //To Do:[implement this function]
            return base.GetHashCode();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            EnStack<T> obj = new EnStack<T>();
            if ((typeof(T)).IsPrimitive || typeof(T).IsEnum || typeof(T) == typeof(string))
            {
                foreach (T o in theStack)
                    obj.theStack.Add(o);
            }
            else
            {
                foreach (object o in theStack)
                    obj.theStack.Add((T)((o as ICloneable).Clone()));
            }
            return obj;
        }

        #region IState Members

        /// <summary>
        /// 
        /// </summary>
        public object[] Fields
        {
            get { throw new NotImplementedException(); }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="locationId"></param>
        /// <returns></returns>
        public string GetLocationName(int locationId)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        public int LocationValuesCount
        {
            get { throw new NotImplementedException(); }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="locationId"></param>
        /// <returns></returns>
        public object GetLocation(int locationId)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComparable Members

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public int CompareTo(object obj)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}